import 'dart:convert';

import 'package:XinXiZiXun/common/global/global.dart';
import 'package:XinXiZiXun/common/values/values.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/material.dart';

///网络请求
class HttpUtil {
  static const int CONNECT_TIMEOUT = 5000;
  static const int RECEIVE_TIMEOUT = 3000;

  //工厂方法构造函数
  factory HttpUtil() => _getInstance();

  // instance的getter方法，HttpUtil.instance获取对象
  static HttpUtil get instance => _getInstance();

  //静态变量_instance, 存储唯一对象
  static HttpUtil _instance;

  //获取对象
  static HttpUtil _getInstance() {
    if (_instance == null) {
      //使用私有构造方法创建
      _instance = HttpUtil._internal();
    }
    return _instance;
  }

  Dio dio;
  CancelToken _cancelToken = CancelToken();

  // 私有的自定义命名式构造方法，通过它实现一个类;
  // 子类不能继承internal
  // 不是关键字，可定义其他名字
  HttpUtil._internal() {
    //基本配置
    BaseOptions options = BaseOptions(
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: CONNECT_TIMEOUT,

      // 响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: RECEIVE_TIMEOUT,

      /// 请求的Content-Type，默认值是"application/json; charset=utf-8".
      /// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
      /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,  这样[Dio]
      /// 就会自动编码请求体.
      contentType: 'application/json; charset=utf-8',

      /// [responseType] 表示期望以那种格式(方式)接受响应数据。
      /// 目前 [ResponseType] 接受三种类型 `JSON`, `STREAM`, `PLAIN`.
      ///
      /// 默认值是 `JSON`, 当响应头中content-type为"application/json"时，dio 会自动将响应内容转化为json对象。
      /// 如果想以二进制方式接受响应数据，如下载一个二进制文件，那么可以使用 `STREAM`.
      ///
      /// 如果想以文本(字符串)格式接收响应数据，请使用 `PLAIN`.
      responseType: ResponseType.json,

      // Http请求头.
      headers: {},

      // 请求基地址,可以包含子路径
      baseUrl: Service.BaseUrl,
    );

    dio = Dio(options);

    //cookie管理
    CookieJar cookieJar = CookieJar();
    dio.interceptors.add(CookieManager(cookieJar));

    //拦截器
    dio.interceptors
        .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
      return options;
    }, onResponse: (Response response) {
      return response;
    }, onError: (DioError err) {
      ErrorInterceptor errorInterceptor = ErrorInterceptor();
      errorInterceptor.onError(err);
    }));
    //加内存缓存
//    dio.interceptors.add(NetCache());
  }

  //取消请求 同一个cancelToken取消可用于多个请求取消
  void cancelRequests(CancelToken token) {
    token.cancel('cancelOperation');
  }

  ///读取本地配置
  Map<String, dynamic> getAuthorizationHeader(String timeStamp, String sign) {
    var headers;

    String accessToken = Global.profile?.sessionToken;
    if (accessToken != null) {
      headers = {
        'AccessToken': '$accessToken',
        'Content-Type': 'application/json',
        'JiRuiAppId': Service.SHA256Key,
        'JiRuiTimeStamp': timeStamp,
        'JiRuiSign': sign,
      };
    } else {
      headers = {
        'Content-Type': 'application/json',
        'JiRuiAppId': Service.SHA256Key,
        'JiRuiTimeStamp': timeStamp,
        'JiRuiSign': sign,
      };
    }
    return headers;
  }

  //List比对排序
  String sortParams(Map<String, dynamic> map) {
    List keys = map.keys.toList();

    print('前排列---: ${keys}');

    keys.sort((a, b) => (a.compareTo(b)));

    print('后排列---: ${keys}');

    String resultString = '';
    keys.forEach((element) {
      resultString = resultString + element + map[element];
      print('拼接字符串---: ${resultString}');
    });

    return resultString;
  }

  //获取当前时间戳
  String getCurrentTime() {
    int currentTime = DateTime.now().millisecondsSinceEpoch;
    String currentTimeString = currentTime.toString();
    currentTimeString = currentTimeString.substring(0, 10);
    print('当前时间戳======: ${currentTimeString}');
    return currentTimeString;
  }

  //签名SHA256
  String signSHASecrect(String option1, String option2) {
    String message = option1 + option2;

    print('加密前---：${message}');

    var key = utf8.encode(Service.SHA256Secrect);
    var bytes = utf8.encode(message);

    var hmacSha256 = new Hmac(sha256, key); // HMAC-SHA256
    var digest = hmacSha256.convert(bytes);

//    print("HMAC digest as bytes: ${digest.bytes}");
//    print("HMAC digest as hex string: $digest");

    print('加密后---：${digest.toString()}');

    return digest.toString();
  }

  ///Post请求
  Future post(
    String path, {
    dynamic params,
    Options options,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions = requestOptions.merge(extra: {});

    //时间戳
    String currentTimeString = getCurrentTime();
    //参数排序
    String sortString = sortParams(params);
    //签名加密
    String sign = signSHASecrect(sortString, currentTimeString);

    Map<String, dynamic> _authorization =
        getAuthorizationHeader(currentTimeString, sign);
    if (_authorization != null) {
      requestOptions = requestOptions.merge(headers: _authorization);
    }

    var response = await dio.post(Service.BaseUrl + path,
        data: params, options: requestOptions, cancelToken: _cancelToken);

    // print('===:' + response.toString());

    return response.data;
  }
}

///Error拦截器(错误处理)
class ErrorInterceptor extends Interceptor {
  @override
  Future onError(DioError err) {
    //error统一处理
    AppException appException = AppException.create(err);
    //错误提示
    debugPrint('DioError---: ${appException.toString()}');

    err.error = appException;

    return super.onError(err);
  }
}

///异常处理
class AppException implements Exception {
  //自定义异常
  final int _code;
  final String _message;

  AppException([this._code, this._message]);

  String toString() {
    return "$_code & $_message";
  }

  factory AppException.create(DioError error) {
    switch (error.type) {
      case DioErrorType.CANCEL:
        {
          return BaseRequestException(-1, '请求取消');
        }
        break;

      case DioErrorType.CONNECT_TIMEOUT:
        {
          return BaseRequestException(-1, '连接超时');
        }
        break;

      case DioErrorType.SEND_TIMEOUT:
        {
          return BaseRequestException(-1, '请求超时');
        }
        break;

      case DioErrorType.RECEIVE_TIMEOUT:
        {
          return BaseRequestException(-1, '响应超时');
        }
        break;

      case DioErrorType.RESPONSE:
        {
          try {
            int errorCode = error.response.statusCode;

            switch (errorCode) {
              case 400:
                {
                  return BaseRequestException(errorCode, '请求语法错误');
                }
                break;

              case 401:
                {
                  return UnauthorisedException(errorCode, '没有权限');
                }
                break;

              case 403:
                {
                  return UnauthorisedException(errorCode, '服务器拒绝执行');
                }
                break;

              case 404:
                {
                  return UnauthorisedException(errorCode, '无法连接服务器');
                }
                break;

              case 405:
                {
                  return UnauthorisedException(errorCode, '请求方法被禁止');
                }
                break;

              case 500:
                {
                  return UnauthorisedException(errorCode, '服务器内部错误');
                }
                break;

              case 502:
                {
                  return UnauthorisedException(errorCode, '无效的请求');
                }
                break;

              case 503:
                {
                  return UnauthorisedException(errorCode, '服务器挂了');
                }
                break;

              case 505:
                {
                  return UnauthorisedException(errorCode, '不支持HTTP协议请求');
                }
                break;

              default:
                {
                  return UnauthorisedException(
                      errorCode, error.response.statusMessage);
                }
            }
          } on Exception catch (_) {
            return AppException(-1, '未知错误');
          }
        }
        break;

      default:
        {
          return AppException(-1, error.message);
        }
    }
  }
}

///请求错误
class BaseRequestException extends AppException {
  BaseRequestException([int code, String message]) : super(code, message);
}

///未认证异常
class UnauthorisedException extends AppException {
  UnauthorisedException([int code, String message]) : super(code, message);
}
